package pub.tbc.toolkit;

import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pub.tbc.toolkit.function.Consumer;
import pub.tbc.toolkit.function.Function;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * @author tbc
 * @version 1.0 {2016年6月29日 下午4:59:24}
 */
public final class PropsUtil {
    private static Logger log = LoggerFactory.getLogger(PropsUtil.class);

    private PropsUtil() {
        throw new AssertionError("No " + getClass().getName() + " instances for you!");
    }

    /**
     * 加载默认配置文件： classpath:config.properties
     *
     * @return
     * @author tbc
     * @version 1.0 {2016年7月12日 下午1:49:34}
     */
    public static Properties load() {
        return load("config.properties");
    }

    /**
     * 加载属性文件
     *
     * @param filename
     * @return
     * @author tbc
     * @version 1.0 {2016年6月29日 下午5:07:34}
     */
    public static Properties load(String filename) {
        Properties props = null;
        try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename)) {
            if (is == null) {
                throw new FileNotFoundException(filename + " file is not found");
            }
            props = new Properties();
            props.load(is);
        } catch (Exception e) {
            log.error("load properties file failure ", e);
        }
        return props;
    }

    /**
     * 从绝对路径加载配置文件
     *
     * @param path
     * @param filename
     * @return
     * @author tbc
     * @version 1.0 {2016年7月12日 下午1:49:08}
     */
    public static Properties load(String path, String filename) {
        String file = filename.endsWith(".properties") ? filename : filename + "properties";
        return load(path + File.separator + file);
    }

    public static String getString(Properties p, String key) {
        return getString(p, key, "");
    }

    /**
     * 获取字符型属性（可指定默认值）
     *
     * @param p
     * @param key
     * @param defaultValue
     * @return
     * @author tbc
     * @version 1.0 {2016年6月29日 下午5:10:03}
     */
    public static String getString(Properties p, String key, String defaultValue) {
        return getValueByType(p, key, defaultValue, new Function<String, String>() {
            @Override
            public String apply(String input) {
                return input;
            }
        });
    }

    public static int getInt(Properties p, String key) {
        return getInt(p, key, 0);
    }

    public static int getInt(final Properties p, String key, int defaultValue) {
        return getValueByType(p, key, defaultValue, new Function<String, Integer>() {
            @Override
            public Integer apply(String input) {
                return CastUtil.castInt(input);
            }
        });
    }

    public static double getDouble(Properties p, String key) {
        return getDouble(p, key, 0.0);
    }

    public static double getDouble(final Properties p, String key, double defaultValue) {
        return getValueByType(p, key, defaultValue, new Function<String, Double>() {
            @Override
            public Double apply(String input) {
                return CastUtil.castDouble(input);
            }
        });
    }

    public static boolean getBoolean(Properties p, String key) {
        return getBoolean(p, key, false);
    }

    public static boolean getBoolean(final Properties p, String key, Boolean defaultValue) {
        return getValueByType(p, key, defaultValue, new Function<String, Boolean>() {
            @Override
            public Boolean apply(String input) {
                return CastUtil.castBoolean(input);
            }
        });
    }

    public static <T> T getValueByType(Properties p, final String key, T defaultValue, Function<String, T> function) {
        T value = defaultValue;
        if (p.containsKey(key)) {
            value = function.apply(p.getProperty(key));
        }
        return value;
    }

    public static Map<String, String> filterKey(final Properties p, Predicate<String> predicate) {
        if (Objs.isEmpty(p)) {
            return null;
        }
        Set<String> keys = p.stringPropertyNames();
        final Set<String> newkeys = FluentIterable.from(keys).filter(predicate).toSet();
        final Map<String, String> map = Maps.newHashMap();
        Loops.forEach(newkeys, new Consumer<String>() {
            @Override
            public void accept(String k) {
                map.put(k, p.getProperty(k));
            }
        });
        return map;
    }

    public static Map<String, String> getMap(Properties p, final String prefix) {
        return filterKey(p, new Predicate<String>() {
            @Override
            public boolean apply(String input) {
                return input.startsWith(prefix);
            }
        });
    }

    public static Map<String, String> getMap(Properties p) {
        return filterKey(p, new Predicate<String>() {
            @Override
            public boolean apply(String input) {
                return true;
            }
        });
    }
}
